package weiyao.xinxidasai.Utils.OSS;

import com.aliyun.oss.OSS;
import com.aliyun.oss.event.ProgressEvent;
import com.aliyun.oss.event.ProgressEventType;
import com.aliyun.oss.event.ProgressListener;
import com.aliyun.oss.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;

@Component
public class OSSUploader {

    private final OSS ossClient;
    private final String bucketName;
    private final int chunkSize;

    @Autowired
    public OSSUploader(OSS ossClient,
                       @Value("${oss.bucket-name}") String bucketName,
                       @Value("${oss.chunk-size-mb:10}") int chunkSizeMb) {
        this.ossClient = ossClient;
        this.bucketName = bucketName;
        this.chunkSize = 512* 1024;
    }

    /**
     * 安全上传文件到OSS（带进度显示）
     */
    public void uploadToOSS(File file, String objectKey) {
        // 小文件直接上传
        if (file.length() <= chunkSize) {
            uploadSmallFile(file, objectKey);
        }
        // 大文件分片上传（带进度监控）
        else {
            uploadLargeFile(file, objectKey);
        }
    }

    // ============== 分片上传方法 ==============

    /**
     * 初始化分片上传
     */
    public String initiateMultipartUpload(String objectKey) {
        // 确保objectKey有效
        if (objectKey == null || objectKey.trim().isEmpty()) {
            throw new IllegalArgumentException("Object Key must not be empty");
        }

        InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, objectKey);
        InitiateMultipartUploadResult result = ossClient.initiateMultipartUpload(request);
        return result.getUploadId();
    }

    /**
     * 上传分片
     * @param uploadId 分片上传ID
     * @param partNumber 分片序号
     * @param inputStream 分片数据流
     * @param partSize 分片大小
     * @return ETag标识
     */
    public String uploadPart(String uploadId, String objectKey,int partNumber,
                             InputStream inputStream, long partSize) {
        UploadPartRequest request = new UploadPartRequest();
        request.setBucketName(bucketName);
        request.setUploadId(uploadId);
        request.setInputStream(inputStream);
        request.setPartSize(partSize);
        request.setPartNumber(partNumber);
        request.setKey(objectKey); // 必须设置的对象键
        UploadPartResult result = ossClient.uploadPart(request);
        return result.getPartETag().getETag();
    }

    public String completeMultipartUpload(String uploadId, String objectKey,
                                          List<PartETag> partETags) {
        CompleteMultipartUploadRequest request = new CompleteMultipartUploadRequest(
                bucketName, objectKey, uploadId, partETags);
        CompleteMultipartUploadResult result = ossClient.completeMultipartUpload(request);
        return result.getKey();
    }

    /**
     * 中止分片上传
     * @param uploadId 分片上传ID
     */
    public void abortMultipartUpload(String uploadId) {
        AbortMultipartUploadRequest request = new AbortMultipartUploadRequest(
                bucketName, "", uploadId);
        ossClient.abortMultipartUpload(request);
    }

    // ============== 原有方法保持不变 ==============

    // 小文件上传（确保流正确关闭）
    private void uploadSmallFile(File file, String objectKey) {
        try (InputStream inputStream = new FileInputStream(file)) {
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(file.length());
            metadata.setContentType(determineContentType(file.getName()));

            ossClient.putObject(bucketName, objectKey, inputStream, metadata);
            System.out.println("[" + objectKey + "] 上传完成: 100%");
        } catch (Exception e) {
            throw new RuntimeException("OSS小文件上传失败", e);
        }
    }

    // 大文件分片上传（优化后的设置）
    private void uploadLargeFile(File file, String objectKey) {
        // 创建上传请求
        UploadFileRequest request = new UploadFileRequest(bucketName, objectKey);
        request.setUploadFile(file.getAbsolutePath());
        request.setPartSize(256 * 1024);  // 减小分片大小到256KB
        request.setTaskNum(20);            // 增加并发线程数到20
        request.setEnableCheckpoint(true); // 开启断点续传

        // 设置元数据
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentType(determineContentType(file.getName()));
        request.setObjectMetadata(metadata);

        // 添加进度监听器
        final long totalBytes = file.length();
        final AtomicLong uploadedBytes = new AtomicLong(0);
        request.setProgressListener(new ProgressListener() {
            @Override
            public void progressChanged(ProgressEvent progressEvent) {
                if (progressEvent.getEventType() == ProgressEventType.REQUEST_BYTE_TRANSFER_EVENT) {
                    uploadedBytes.addAndGet(progressEvent.getBytes());
                }
                printProgress(uploadedBytes.get(), totalBytes, objectKey);
            }
        });

        try {
            System.out.println("[" + objectKey + "] 开始分片上传...");
            ossClient.uploadFile(request);
            System.out.println("\n[" + objectKey + "] 上传完成: 100%");
        } catch (Throwable e) {
            throw new RuntimeException("OSS分片上传失败", e);
        }
    }

    // 实时打印上传进度
    private void printProgress(long uploaded, long total, String objectKey) {
        double percentage = (uploaded * 100.0) / total;
        System.out.printf("\r[" + objectKey + "] 上传进度: %.2f%% (%d/%d MB)",
                percentage,
                uploaded / (1024 * 1024),
                total / (1024 * 1024));
    }

    // 根据文件名确定内容类型
    private String determineContentType(String fileName) {
        String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
        switch (extension) {
            case "doc": return "application/msword";
            case "docx": return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
            case "xls": return "application/vnd.ms-excel";
            case "xlsx": return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            case "pdf": return "application/pdf";
            case "txt": return "text/plain";
            case "jpg":
            case "jpeg": return "image/jpeg";
            case "png": return "image/png";
            default: return "application/octet-stream";
        }
    }
}